#define PrynEditorInternal
#define PrynEditorImplementation
#include <prynEditor.h>

static bool prynEditorComponentEachPinInList (PrynEditorComponent *component, PrynPinList *list, int x, int y, bool reverse, bool (*each) (PrynEditorComponent *component, PrynPin *pin, int x, int y, void *data), void *data)
{
	PrynPin *pin;

	if (reverse)
	{
		size_t count;
		prynPinListCount (list, &count);
		x -= count * prynEditorPinStride + prynEditorPinStride - prynEditorPinSize;
	}
	else
		x += prynEditorPinStride - prynEditorPinSize + 2;
	y -= prynEditorPinSizeHalf;
	
	for (pin = list->first; pin; prynPinNext (pin, &pin))
	{
		if (!each (component, pin, x, y, data))
			return false;
		x += prynEditorPinStride;
	}
	
	return true;
}

/// Iterate over all the output pins in the component.
PrynEditorExport (bool, prynEditorComponentEachOutputPin, (PrynEditorComponent *editor, bool (*each) (PrynEditorComponent *component, PrynPin *pin, int x, int y, void *data), void *data))
{
	PrynPinList *list;

	if (!editor) return true;
	prynComponentOutputPins (editor->component, &list);
	return prynEditorComponentEachPinInList (editor, list, editor->x + editor->w, editor->y + editor->h, true, each, data);
}

/// Iterate over all the input pins in the component.
PrynEditorExport (bool, prynEditorComponentEachInputPin, (PrynEditorComponent *editor, bool (*each) (PrynEditorComponent *component, PrynPin *pin, int x, int y, void *data), void *data))
{
	PrynPinList *list;

	if (!editor) return true;
	prynComponentInputPins (editor->component, &list);
	return prynEditorComponentEachPinInList (editor, list, editor->x, editor->y, false, each, data);
}

/// Iterate over all the pins in the component.
PrynEditorExport (bool, prynEditorComponentEachPin, (PrynEditorComponent *component, bool (*each) (PrynEditorComponent *component, PrynPin *pin, int x, int y, void *data), void *data))
{
	return prynEditorComponentEachInputPin (component, each, data) && prynEditorComponentEachOutputPin (component, each, data);
}

PrynResult destroyEditorPin (PrynTag *tag)
{
	PrynEditorPin *editor;
	PrynState *state;

	prynTagData (tag, (void **) &editor);
	state = editor->board->state;
	
	prynEditorPathDestroyRoute (state, &editor->route);
	return 0;
}

//bool PrynImport prynEditorComponentEachPin (PrynEditorComponent *component, bool (*each) (PrynEditorComponent *component, PrynPin *pin, int x, int y, void *data), void *data)

bool eachUpdatePinLocation (PrynEditorComponent *component, PrynPin *pin, int x, int y, void *data)
{
	PrynEditorPin *editor = 0;
	prynEditorPinFrom (&editor, component, pin);
	if (!editor) return true;
	editor->x = x + prynEditorPinSizeHalf;
	editor->y = y + prynEditorPinSizeHalf;
	return true;
}

PrynEditorExport (PrynResult, prynEditorComponentUpdatePins, (PrynEditorComponent *component))
{
	if (!component) return PrynResultNullArgument;
	prynEditorComponentEachPin (component, &eachUpdatePinLocation, 0);
	return PrynResultSuccess;
}

PrynEditorExport (PrynResult, prynEditorPinFrom, (PrynEditorPin **output, PrynEditorComponent *component, PrynPin *pin))
{
	PrynStartFunction ();
	PrynString id;
	PrynState *pinState = 0;
	PrynEditorBoard *board;
	PrynEditorPin *editor = 0;
	PrynTagList *tags;
	PrynObject object;
	PrynTag *tag;
	
	if (output) *output = 0;
	if (pin) prynPinState (pin, &state);
	else if (component) state = component->board->state;
	
	prynAssert (pin && component, NullArgument);
	board = component->board;
	prynPinTags (pin, &tags);
	prynTagFindDataByKey ((void **) &editor, tags, board);
	
	if (editor) prynReturnSuccess ();
	prynPinState (pin, &pinState);
	prynAssert (board->state == pinState, BadArgument);
	
	prynCall (prynAllocateClearedObject (state, &editor));
	prynStringLiteral (&id, "PrynEditor.EditorPin:Board");
	prynPinToObject (pin, &object);
	prynCall (prynTagCreateWithKey (&tag, &object, board, &id, editor));
	editor->component = component;
	editor->board = board;
	editor->pin = pin;
	prynTagSetDestroyFunction (tag, &destroyEditorPin);
	prynEditorComponentUpdatePins (component);

	PrynEndFunctionAfterSuccess ({
		if (output) *output = editor;
	});
}

PrynEditorExport (PrynResult, prynEditorPinUpdateRoute, (PrynEditorPin *editor))
{
	if (!editor) return PrynResultNullArgument;
	prynEditorPathDestroyRoute (editor->board->state, &editor->route);
	if (PrynIsTrue (prynPinIsOutput (editor->pin)) && PrynIsTrue (prynPinIsConnected (editor->pin)))
	{
		PrynPin *connected;
		PrynComponent *connectedComponent;
		PrynEditorComponent *connectedComponentEditor;
		PrynEditorPin *other = 0;
		
		prynPinConnected (editor->pin, &connected);
		prynPinComponent (connected, &connectedComponent);
		prynEditorComponentFrom (&connectedComponentEditor, editor->board, connectedComponent);
		prynEditorPinFrom (&other, connectedComponentEditor, connected);
		
		if (other)
			return prynEditorPathComputeRoute (editor->board->state, &editor->board->path, editor->x, editor->y, other->x, other->y, &editor->route);
	}
	
	return PrynResultDone;
}

